package org.kur.modularization.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import javafx.util.Callback;
import javafx.util.Duration;
import org.kur.modularization.util.Utils;
import org.kur.modularization.cell.JarPathViewCell;
import org.kur.modularization.pojo.AllJar;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static org.kur.modularization.util.Utils.getOutPutFileName;


public class AppController {

    @FXML
    public TextArea exportPackage;

    @FXML
    private CheckBox addClassToJar;

    @FXML
    private TextField artifactId;

    @FXML
    private CheckBox compileInfo;

    @FXML
    private CheckBox createInfo;

    @FXML
    private TextField fileInput;

    @FXML
    private TextField groupId;

    @FXML
    private CheckBox isMvnInstall;

    @FXML
    private TableColumn<AllJar, String> serialNumber;

    @FXML
    private TableColumn<AllJar, String> jarName;

    @FXML
    private TableColumn<AllJar, String> jarPackagePath;

    @FXML
    private TableColumn<AllJar, String> controls;

    @FXML
    private Label msg;

    @FXML
    private TableView<AllJar> tableView;

    @FXML
    private TextField version;
    public static File jarFile;

    private final ObservableList<AllJar> jarList = FXCollections.observableArrayList();

    private String moduleName;

    @FXML
    private Button run;

    @FXML
    private LogController logController;
    @FXML
    private MavenController mavenController;

    @FXML
    void initialize() {
        groupId.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) msg.setText("");
        });
        artifactId.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) msg.setText("");
        });
        version.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue) msg.setText("");
        });

        createInfo.selectedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                compileInfo.setSelected(false);
            }
        });

        compileInfo.selectedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                addClassToJar.setSelected(false);
            }
        });

        addClassToJar.selectedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue) {
                isMvnInstall.setSelected(false);
            }
        });


        Label label = new Label("请添加主jar包依赖的jar包，可多选。");
        label.setFont(Font.font(20));
        label.getStyleClass().addAll("accent");
        Button button = new Button("点击多选添加jar包");
        button.getStyleClass().addAll("accent", "button-outlined");
        button.setOnAction(clickedTableView());
        VBox box = new VBox(label, button);
        box.setSpacing(10);
        box.setAlignment(Pos.CENTER);
        tableView.setItems(jarList);
        tableView.setPlaceholder(box);

        addMsg("使用前须知：\n");
        addMsg("1.使用此工具必须已经配置Java环境变量,且Java版本必读大于9\n");
        addMsg("2.使用此工具必须已经配置Maven环境变量\n");
        addMsg("3.此工具对已经模块化的jar包无效\n");

        serialNumber.setCellFactory(JarPathViewCell.getSerialNumber());
        serialNumber.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getJarName()));

        jarName.setCellFactory(JarPathViewCell.getJarName());
        jarName.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getJarName()));

        jarPackagePath.setCellFactory(JarPathViewCell.getJarPackagePath());
        jarPackagePath.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getJarPath()));
        controls.setCellFactory(new Callback<>() {
            @Override
            public TableCell<AllJar, String> call(TableColumn<AllJar, String> param) {
                return new TableCell<>() {
                    @Override
                    protected void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        if (item != null && !empty) {
                            Button addButton = new Button("添加");
                            addButton.setOnAction(clickedTableView());
                            Button deleteButton = new Button("删除");
                            deleteButton.setOnAction(event -> {
                                tableView.getItems().remove(getIndex());
                                tableView.refresh();
                            });
                            HBox hBox = new HBox(addButton, deleteButton);
                            hBox.setSpacing(3);
                            setGraphic(hBox);
                        }
                    }
                };
            }
        });
        controls.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getJarPath()));


    }

    /**
     * 打印依赖
     *
     * @param groupId    组 ID
     * @param artifactId 唯一标识
     * @param version    版本
     * @param moduleName 模块名称
     */
    public void printDependency(String groupId, String artifactId, String version, String moduleName) {

        addMsg("-------------------------------------------------------");
        addMsg("");
        addMsg("<dependency>");
        addMsg("  <groupId>" + groupId + "</groupId>");
        addMsg("  <artifactId>" + artifactId + "</artifactId>");
        addMsg("  <version>" + version + "</version>");
        addMsg("</dependency>");
        addMsg("");
        if (moduleName != null) addMsg("requires " + moduleName + ";");
        addMsg("");
        addMsg("-------------------------------------------------------");
    }


    private void addMsg(String msg) {
        Platform.runLater(() -> logController.logArea.appendText(msg + "\n"));
    }


    @FXML
    void chooseFile() {

        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("请选择非模块化的Jar包");
        fileChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
        FileChooser.ExtensionFilter jar = new FileChooser.ExtensionFilter("非模块化jar包", "*.jar");
        fileChooser.getExtensionFilters().add(jar);
        Window window = fileInput.getParent().getScene().getWindow();
        File file = fileChooser.showOpenDialog(window);
        if (file != null) {
            jarFile = file;
            fileInput.setText(file.getAbsolutePath());
        } else {
            addMsg("未选择jar包");
        }
    }

    @FXML
    void showMavenPane() {
        TranslateTransition transition = new TranslateTransition(Duration.seconds(0.6), mavenController.mavenRootPane);
        transition.setFromY(700);
        transition.setToY(340);
        transition.play();
    }


    public EventHandler<ActionEvent> clickedTableView() {
        return event -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("请选择非模块化的Jar包");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
            FileChooser.ExtensionFilter jar = new FileChooser.ExtensionFilter("非模块化jar包", "*.jar");
            fileChooser.getExtensionFilters().add(jar);
            Window window = fileInput.getParent().getScene().getWindow();
            List<File> jarFileList = fileChooser.showOpenMultipleDialog(window);
            if (jarFileList != null) {
                if (!jarFileList.isEmpty()) {
                    for (File file : jarFileList) {
                        jarList.add(new AllJar(file.getName(), file.getAbsolutePath()));
                    }
                }
            } else {
                addMsg("未选择jar包");
            }
        };
    }

    @FXML
    void run() {

        Service<String> service = new Service<>() {
            @Override
            protected Task<String> createTask() {
                return new Task<>() {
                    @Override
                    protected String call() {
                        //第一步：解压主jar包
                        File decompression = Utils.decompression(jarFile);
                        //第二歩：解压所有附加的jar包,并把解压后的文件夹里的所有东西复制到主jar包的解压文件夹里
                        List<File> fileList = new ArrayList<>();
                        if (!jarList.isEmpty()) {
                            for (AllJar allJar : jarList) {
                                File file = Utils.decompression(new File(allJar.getJarPath()));
                                for (File listFile : Objects.requireNonNull(file.listFiles())) {
                                    if (listFile.getName().equals("META-INF")) {
                                        FileUtil.del(listFile);
                                    } else {
                                        File copy = FileUtil.copy(listFile, decompression, true);
                                        fileList.add(new File(copy, listFile.getName()));
                                    }
                                }
                            }
                        }

                        if (createInfo.isSelected()) {
                            //第三步：生成module-info.java文件
                            runCommand(decompression, "cmd.exe", "/c", "jdeps", "--ignore-missing-deps", "--generate-open-module", ".", jarFile.getAbsolutePath());

                            if (moduleName != null) {
                                if (compileInfo.isSelected()) {
                                    //第四步：编译module-info.java文件生成class文件
                                    //生成的module-info.java文件目录
                                    File info = new File(jarFile.getParent() + File.separator + moduleName + File.separator + "module-info.java");

                                    writerExports(info);

                                    //把module-info.java文件复制到解压后的目录
                                    File mainJarFile = FileUtil.copy(info, decompression, true);
                                    //编译
                                    runCommand(mainJarFile, "cmd.exe", "/c", "javac", "-d", ".", "module-info.java");

                                    if (addClassToJar.isSelected()) {
                                        //第五步：把class文件加入到jar中
                                        File classFile = new File(mainJarFile.getParent() + File.separator + "module-info.class");
                                        FileUtil.copy(classFile, decompression.getParentFile(), true);
                                        runCommand(jarFile, "cmd.exe", "/c", "jar", "uf", jarFile.getName(), classFile.getName());
                                        for (File file : fileList) {
                                            System.out.println("file = " + file.getAbsolutePath());
                                            FileUtil.copy(file, jarFile.getParentFile(), true);
                                            runCommand(jarFile, "cmd.exe", "/c", "jar", "uf", jarFile.getName(), file.getName());
                                        }

                                        if (isMvnInstall.isSelected()) {
                                            //第六步：把jar包添加到maven本地仓库。
                                            String command = "mvn install:install-file -Dfile=" + jarFile.getAbsolutePath() + " -DgroupId=" + groupId.getText() + " -DartifactId=" + artifactId.getText() + " -Dversion=" + version.getText() + " -Dpackaging=jar";
                                            System.out.println(command);
                                            runCommand(new File(System.getProperty("user.home")), "cmd.exe", "/c", command);
                                            printDependency(groupId.getText(), artifactId.getText(), version.getText(), moduleName);
                                        }
                                    }
                                }
                            }
                        }

                        return null;
                    }
                };
            }
        };

        service.stateProperty().addListener((observable, oldValue, newValue) -> {
            addMsg("任务运行状态：" + newValue.toString());
            if (newValue.equals(Worker.State.RUNNING)) {
                run.setDisable(true);
            } else if (newValue.equals(Worker.State.FAILED) || newValue.equals(Worker.State.SUCCEEDED) || newValue.equals(Worker.State.READY)) {
                run.setDisable(false);
            }
        });

        logController.logArea.textProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null && !newValue.contains("此jar包是已经模块化的jar包")) {
                if (newValue.contains("writing to")) {
                    moduleName = getOutPutFileName(newValue);
                } else if (newValue.contains("无法使用 --generate-module-info 选项指定的模块化 JAR 文件")) {
                    addMsg("此jar包是已经模块化的jar包");
                    service.cancel();
                    service.reset();

                }
            }
        });


        if (StrUtil.isNotEmpty(groupId.getText())) {

            if (StrUtil.isNotEmpty(artifactId.getText())) {

                if (StrUtil.isNotEmpty(version.getText())) {

                    if (StrUtil.isNotEmpty(fileInput.getText())) {

                        if (service.getState().equals(Worker.State.READY)) {
                            service.start();
                        } else {
                            if (service.getState().equals(Worker.State.SUCCEEDED)) {
                                service.start();
                            } else if (service.getState().equals(Worker.State.FAILED)) {
                                service.reset();
                            } else if (service.getState().equals(Worker.State.RUNNING)) {
                                msg.setText("请等待上一次执行完成");
                            }
                        }

                    } else {
                        msg.setText("请选择jar包");
                    }

                } else {
                    msg.setText("请输入version");
                }

            } else {
                msg.setText("请输入artifactId");
            }

        } else {
            msg.setText("请输入groupId");
        }

    }

    @FXML
    void showLogPane() {
        TranslateTransition transition = new TranslateTransition(Duration.seconds(0.6), logController.logRootPane);
        transition.setFromY(700);
        transition.setToY(300);
        transition.play();

    }

    /**
     * 将导出的包写入module-info.java文件
     *
     * @param info module-info.java文件
     */
    private void writerExports(File info) {
        StringBuffer sb = new StringBuffer();
        try (BufferedReader reader = new BufferedReader(new FileReader(info))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("}")) {
                    sb.append(exportPackage.getText())
                            .append("\r");
                }
                sb.append("\n").append(line);
            }
            System.out.println(sb);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 写入文件
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(info))) {
            writer.write(sb.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public void runCommand(File runFile, String... command) {
        try {
            // 创建ProcessBuilder对象
            ProcessBuilder builder = new ProcessBuilder(command);
            builder.redirectErrorStream(true); // 将标准错误流和标准输出流合并为一个流
            builder.directory(runFile.getParentFile());
            // 执行命令并获取结果
            Process process = builder.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.forName("GBK")));
            String line;
            while ((line = reader.readLine()) != null) {
                addMsg(line);
            }
            reader.close();
            process.destroy();
        } catch (IOException e) {
            addMsg(e.getMessage());
        }
    }

}


